package com.shop.cereshop.app.service.nearbyUser;

import com.shop.cereshop.app.param.nearby.NearbySearchParam;
import com.shop.cereshop.commons.domain.buyer.CereBuyerUser;
import com.shop.cereshop.commons.domain.buyer.NearByUserInfoVo;
import com.shop.cereshop.commons.domain.common.Page;
import com.shop.cereshop.commons.enums.RedisEnum;
import com.shop.cereshop.commons.utils.GeoHashUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("NearbyUserByRedisService")
public class NearbyUserByRedisServiceImpl implements NearbyUserService {


    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 更新用户地理位置
     *
     * @param userId    用户ID
     * @param longitude 经度
     * @param latitude  纬度
     */
    @Override
    public Long updateUserLocation(Long userId, Double longitude, Double latitude) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        // 参数：KEY, Point(经度, 纬度), member
        Point point = new Point(longitude, latitude);
        // 2. 更新最后活跃时间戳（以当前时间戳作为分数）
        double timestamp = System.currentTimeMillis();
        redisTemplate.opsForZSet().add(RedisEnum.USER_LOCATION_TS_KEY.getKey(), userId.toString(), timestamp);
        return geoOps.add(RedisEnum.USER_LOCATION_KEY.getKey(), point, userId.toString());
    }

    /**
     * 查询附近的人 (基于点和半径)
     *
     * @param longitude 当前经度
     * @param latitude  当前纬度
     * @param distance  距离
     * @param unit      单位 (e.g., Metrics.KILOMETERS)
     * @return 附近的人列表（包含距离和坐标）
     */
    @Override
    public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> findNearby(
            Double longitude, Double latitude, Double distance, Metric unit) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();

        // 1. 创建一个圆心和半径
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(distance, unit));

        // 2. 设置查询参数：返回结果包含距离和坐标
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance() // 包含距离
                .includeCoordinates() // 包含坐标
                .sortAscending(); // 按距离升序排序

        // 3. 执行查询
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = geoOps.radius(RedisEnum.USER_LOCATION_KEY.getKey(), circle, args);

        // 4. 将结果转换为 List 返回
        return results.getContent();
    }

    /**
     * 查询附近的人 (基于某个用户)
     *
     * @param userId   目标用户ID
     * @param distance 距离
     * @param unit     单位
     * @return 附近的人列表
     */
    @Override
    public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> findNearbyByUserId(
            Long userId, Double distance, Metric unit) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();

        // 直接使用 member 进行查询
        Distance radius = new Distance(distance, unit);
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()
                .includeCoordinates()
                .sortAscending();

        GeoResults<RedisGeoCommands.GeoLocation<String>> results = geoOps.radius(RedisEnum.USER_LOCATION_KEY.getKey(), userId.toString(), radius, args);
        return results.getContent();
    }

    /**
     * 删除用户地理位置
     *
     * @param userId 用户ID
     */
    @Override
    public void removeUserLocation(Long userId) {
        // GEO 类型底层是 ZSET，所以可以用 ZREM 删除成员
        redisTemplate.opsForZSet().remove(RedisEnum.USER_LOCATION_KEY.getKey(), userId.toString());
    }

    @Override
    public String getUserGeoHash(Long userId, int length) {
        if (userId == null || length <= 0) {
            return null;
        }
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        List<Point> positions = geoOps.position(RedisEnum.USER_LOCATION_KEY.getKey(), userId.toString());
        if (positions == null || positions.isEmpty() || positions.get(0) == null) {
            return null;
        }
        Point point = positions.get(0);
        double longitude = point.getX();
        double latitude = point.getY();
        return GeoHashUtil.encode(latitude, longitude, length);
    }

    @Override
    public boolean isOnline(Long userId, long thresholdMs) {
        if (userId == null) {
            return false;
        }
        Double score = redisTemplate.opsForZSet().score(RedisEnum.USER_LOCATION_TS_KEY.getKey(), userId.toString());
        if (score == null) {
            return false;
        }
        long lastActive = score.longValue();
        return System.currentTimeMillis() - lastActive <= thresholdMs;
    }
}
